package org.wjk.filter;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.wjk.pojo.entity.SysUserEntity;
import org.wjk.pojo.vo.ResponseRes;
import org.wjk.utils.JwtTokenUtils;
import org.wjk.utils.ResponseUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtTokenParse extends OncePerRequestFilter {
    private final JwtTokenUtils tokenUtils;
    private final UserDetailsService detailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        /*该过滤器最终目标:构建一个UsernamePasswordAuthenticationToken的对象,并且通过该类的三个参数的构造函数完成的构建
        * 把该类的对象通过SecurityContextHolder设置到security上下文中.*/

        /*1.请求头中指定的属性里获取到token
        * 2.校验token是否存在,不存在直接放行请求,必然会进入认证管理器
        * 3.如果存在,校验token是否为指定的字符串开头,如果不是,返回前端token无效
        * 4.如果是,从token中获取真正的Jwttoken
        * 5.解析jwtToken,是否抛出了异常,如果抛出异常,响应前端token无效
        * 6.未抛出异常,获取token中保存的username
        * 7.通过username获取user信息,以及user拥有的权限:UserDedialService::loadUserByUsername()
        * 8.通过UsernamePasswordAuthenticationToken的三个参数构造函数构建该类的一个对象
        * 9.把该类对象通过SecurityContextHeader 设置到security上下文中*/

        /*1.从请求的请求头中的Authorization里取出token*/
        /*request.getRequestHeaders()*/
        String token= request.getHeader(tokenUtils.getProps().getHeader());
        log.debug("当前token为{}",token);
        if (token==null){
            filterChain.doFilter(request,response);
            return;
        }
        /*如果存在token,校验token是否为jwtProperties.getHead()的值开头
        如不是响应前端,message:token无效请重新登录,resCode:5002
        如果是以该值开头时token中真jwttoken:AuthorizationeyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTY1NjE1MzUwMH0.hjH8JwbXWb9oXZzAKDzfq3wxMwH_mczsCC1yncCGmUtm4oRp8miO3cW8YUmObGYy9Wom5w9R17SgHOTRl3eAwQ
        startsWith()校验
        * */
        String head=tokenUtils.getProps().getHead();
        if (!token.startsWith(head)){
            ResponseUtils.convertToJson(response, ResponseRes.failure(5002,"token无效,请重新登录",null));
            return;
        }
        token=token.substring(head.length());
        /*解析token:tokenUtils.resolverJwtToken()
        * 获取token中保存的用户名
        * 如果tokenUtiles.resolverJwtToken()抛出异常了,捕获该异常,响应前端 resCode:5002,resMsg:"你的登录已过期请重新登录"
        * 通过用户名调用UserDedialService::loadUserByUsername()方法,获取当前用户的用户信息和拥有的权限.封装到UsernamePasswordAuthetication
        * 将该对象设置到SecurityContext中*/
        String username;
        try {
            username=tokenUtils.resolverJwtToken(token);
        } catch (Exception e) {
            log.debug("解析token时发生异常,具体信息为{}",e.getMessage());
            ResponseUtils.convertToJson(response, ResponseRes.failure(5002,"您的登录已过期,请重新登录",null));
            return;
        }
        SysUserEntity user= (SysUserEntity) detailsService.loadUserByUsername(username);
        UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request,response);
    }
}
